%%%-------------------------------------------------------------------
%%% @author Rain
%%% @doc 常用接口
%%% @end
%%%-------------------------------------------------------------------
-module(game_util).
-feature(maybe_expr, enable).

-author("Rain").

-include("game_util.hrl").
%% API

-export([
    to_integer/1,
    to_atom/1,
    to_list/1,
    to_binary/1,
    term_to_string/1,
    string_to_term/1,
    list_to_existing_atom/1
]).

-export([start_child/2]).
-export([simple_start_child/2, simple_start_child/3,
    simple_start_child/4, simple_start_child/5, simple_start_child/6]).
-export([json_decode/1, json_decode/2]).
-export([json_encode/1, json_encode/2]).

-export([
    get_behaviour_mods/2
]).

%% @doc 简单启动子服务
simple_start_child(SupMod, ChildMod) ->
    simple_start_child(SupMod, ChildMod, worker).
simple_start_child(SupMod, ChildMod, Type) ->
    simple_start_child(SupMod, ChildMod, ChildMod, start_link, [], Type).
simple_start_child(SupMod, ChildMod, Type, StartArgs) ->
    simple_start_child(SupMod, ChildMod, ChildMod, start_link, StartArgs, Type).
simple_start_child(SupMod, ChildId, StartMod, Type, StartArgs) ->
    simple_start_child(SupMod, ChildId, StartMod, start_link, StartArgs, Type).
simple_start_child(SupMod, ChildId, StartMod, StartFun, StartArgs, Type) ->
    ShutDown = ?IF(Type == supervisor, infinity, 5000),
    ChildSpec = #{id => ChildId,
        start => {StartMod, StartFun, StartArgs},
        restart => permanent,
        shutdown => ShutDown,
        type => Type,
        modules => [StartMod]},
    start_child(SupMod, ChildSpec).

%% @doc 启动某个子服务
start_child(Sup, Child) ->
    case catch supervisor:start_child(Sup, Child) of
        {ok, Pid} ->
            {ok, Pid};
        {error, {{already_started, Pid}, _}} ->
            ?UTIL_WARN("child ~p is already started!, sup:~p", [Child, Sup]),
            {ok, Pid};
        Other ->
            throw(Other)
    end.


%% @doc json封包
%% @NOTE: 为了清晰区分json里面的object和array，项目统一使用map去表示
%% eg: util:json_encode(#{<<"message">> => <<"hello world">>}). => {ok,<<"{\"message\":\"hello world\"}">>}
json_encode(EJson) when is_map(EJson) ->
    json_encode(EJson, []).
json_encode(EJson, Options) when is_map(EJson) ->
    try
        JsonData = jiffy:encode(EJson, Options),
        {?ok, JsonData}
    catch
        Class:Reason:Stacktrace ->
            ?UTIL_ERROR("Json Encode Failed ~p ~p ~n ~p~n", [Class, Reason, Stacktrace]),
            {?error, Class, Reason}
    end.

%% @doc json解包
%% @NOTE: 为了清晰区分json里面的object和array，项目统一使用map去表示
%% eg: util:json_decode(<<"{\"message\":\"hello world\"}">>). => {ok,#{<<"message">> => <<"hello world">>}}
json_decode(JsonData) ->
    json_decode(JsonData, []).
json_decode(JsonData, Options) ->
    try
        EJson = jiffy:decode(JsonData, lists:uniq([return_maps | Options])),
        {?ok, EJson}
    catch
        Class:Reason:Stacktrace ->
            ?UTIL_ERROR("Json Decode Failed ~p ~p ~n ~p~n", [Class, Reason, Stacktrace]),
            {?error, Class, Reason}
    end.


%% @doc 获取某个行为模式的模块(默认取当前游戏app模块)
%% return: [Mod1, Mod2 | ...] | []
get_behaviour_mods(FindBehaviour, Application) ->
    case catch application:get_key(Application, modules) of
        {ok, Mods} ->
            get_behaviour_mods1(Mods, FindBehaviour);
        _ ->
            []
    end.

get_behaviour_mods1(Mods, FindBehaviour) ->
    get_behaviour_mods1(Mods, FindBehaviour, []).
get_behaviour_mods1([], _FindBehaviour, AccMods) ->
    AccMods;
get_behaviour_mods1([Mod|T], FindBehaviour, AccMods) ->
    maybe
        [{_, _} | _] = Attributes ?= catch Mod:module_info(attributes),
        {behaviour, BehaviourList} ?= lists:keyfind(behaviour, 1, Attributes),
        ?true ?= lists:member(FindBehaviour, BehaviourList),
        AccMods1 = [Mod | AccMods],
        get_behaviour_mods1(T, FindBehaviour, AccMods1)
    else
        _ ->
            get_behaviour_mods1(T, FindBehaviour, AccMods)
    end.

%% @doc 将不同类型的数据转换为列表
to_list(List) when is_list(List) ->
    List;
to_list(Atom) when is_atom(Atom) ->
    atom_to_list(Atom);
to_list(Binary) when is_binary(Binary) ->
    binary_to_list(Binary);
to_list(Int) when is_integer(Int) ->
    integer_to_list(Int);
to_list(Float) when is_float(Float) ->
    term_to_string(Float);
to_list(Tuple) when is_tuple(Tuple) ->
    tuple_to_list(Tuple);
to_list(_) ->
    [].

%% @doc 将不同类型的数据转换为二进制
to_binary(Msg) when is_binary(Msg) ->
    Msg;
to_binary(Msg) when is_atom(Msg) ->
    list_to_binary(atom_to_list(Msg));
to_binary(Msg) when is_list(Msg) ->
    list_to_binary(Msg);
to_binary(Msg) when is_float(Msg) ->
    list_to_binary(float_to_list(Msg));
to_binary(Msg) when is_integer(Msg) ->
    list_to_binary(integer_to_list(Msg));
to_binary(_Msg) ->
    <<>>.

%% @doc 将数据转换为字符串
term_to_string(Term) ->
    lists:flatten(io_lib:format("~p", [Term])).

%% @doc 将字符串尝试转换回数据
string_to_term(String) ->
    case erl_scan:string(String ++ ".") of
        {?ok, Tokens, _} ->
            case erl_parse:parse_term(Tokens) of
                {?ok, Term} ->
                    Term;
                _Err ->
                    ?undefined
            end;
        _Error ->
            ?undefined
    end.

%% @doc 将不同类型的数据转换为整数
to_integer(Atom) when is_atom(Atom) ->
    list_to_integer(atom_to_list(Atom));
to_integer(List) when is_list(List) ->
    list_to_integer(List);
to_integer(Binary) when is_binary(Binary) ->
    list_to_integer(binary_to_list(Binary));
to_integer(Any) ->
    Any.

%% @doc 将不同类型的数据转换为原子
to_atom(List) when is_list(List) ->
    ?MODULE:list_to_existing_atom(List);
to_atom(Int) when is_integer(Int) ->
    List = integer_to_list(Int),
    ?MODULE:list_to_existing_atom(List);
to_atom(Binary) when is_binary(Binary) ->
    binary_to_atom(Binary);
to_atom(Any) ->
    Any.

list_to_existing_atom(String) ->
    case catch erlang:list_to_existing_atom(String) of
        Atom when is_atom(Atom) ->
            Atom;
        _ ->
            erlang:list_to_atom(String)
    end.

%%persistent_term
%% persistent_term:put

